﻿using SIR.Common.Condition;
using SIR.Entities.Implementations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;

namespace SIR.Web.API
{
	public class SearchController : ApiController
	{
		/// <summary>
		/// Search for images by using another image
		/// </summary>
		/// <param name="tolerance">The tolderance to match for the image search</param>
		/// <param name="imageId">The image Id as the basis of the search</param>
		/// <returns>A response containing the found images</returns>
		[HttpGet]
		public Response<IEnumerable<Image>> Image(int tolerance, long imageId)
		{
			try
			{
				// Tolerance must be greater than zero
				if (tolerance <= 0)
					return Response<IEnumerable<Image>>.CreateResponse(false, new List<Image>(), "Invalid tolerance");

				long? userId;
				// find the user id from the session
				SessionManager.GetSession(HttpContext.Current, out userId, false);
				// Search for the image that was passed to the method
				var imageResults = DAL.DataServer.Find<Image>(new PredicateGroup(new IPredicate[] {
					new Predicate("Id", imageId, PredicateOperator.EqualTo),
					new PredicateGroup(new IPredicate[] {
						new Predicate("UserId", userId.HasValue ? userId.Value : Int32.MinValue, PredicateOperator.EqualTo),
						new PredicateGroup(new [] {
							new Predicate("IsPrivate", false, PredicateOperator.EqualTo),
							new Predicate("IsFolderPrivate", false, PredicateOperator.EqualTo)
						}, true)}, false)
					}, true));
				// An error occured
				if (!imageResults.WasSuccessful)
					return Response<IEnumerable<Image>>.CreateResponse(false, new List<Image>(), imageResults.ErrorMessage);
				// Image not found
				if (!imageResults.Results.Any())
				{
					return Response<IEnumerable<Image>>.CreateResponse(false, new List<Image>(),
						string.Format("There was no image found with the ID of {0}", imageId));
				}
				// Find the analysis record
				var imageAnalysisResults = DAL.DataServer.Find<ImageAnalysis>(new Predicate("ImageId", imageId, PredicateOperator.EqualTo));
				// An error occured
				if (!imageAnalysisResults.WasSuccessful)
					return Response<IEnumerable<Image>>.CreateResponse(false, new List<Image>(), imageAnalysisResults.ErrorMessage);
				// Image analysis not complete
				if (!imageAnalysisResults.Results.Any())
				{
					return Response<IEnumerable<Image>>.CreateResponse(false, new List<Image>(),
						"Image analysis is taking longer than expected. Please wait...");
				}
				// Search By Image
				var result = DAL.DataServer.SearchByImage<Image>(imageAnalysisResults.Results.First().Id, tolerance);
				// Return the results
				return Response<IEnumerable<Image>>.CreateResponse(result.WasSuccessful, result.Results ?? new List<Image>(), result.ErrorMessage);
			}
			catch (Exception ex)
			{
				// An error occured, log the message and return a message back
				DAL.DataServer.LogException(ex.Message);
				return Response<IEnumerable<Image>>.CreateResponse(false, new List<Image>(), "Could not search by image for an unknown reason");
			}
		}
		/// <summary>
		/// Returns a list of images that are found by searching by colors
		/// </summary>
		/// <param name="colors">A string representation of the color search</param>
		/// <returns>A response containing the list of images</returns>
		[HttpGet]
		public Response<IEnumerable<Image>> Color(string colors)
		{
			// Search is invalid if there are not colors specified
			if (string.IsNullOrWhiteSpace(colors))
				return Response<IEnumerable<Image>>.CreateResponse(false, new List<Image>(), "Invalid color definitions");

			try
			{
				var colorList = colors.Split(';');
				IEnumerable<Image> imagesFound = null;
				// For each color in the colors string
				foreach (var colorString in colorList)
				{
					// Parse the color
					var parsed = SearchForColor.Parse(colorString);
					// Search for the images
					var result = DAL.DataServer.SearchByColor<Image>(parsed, parsed.Tolerance, parsed.Percent);
					// If not successful send a fail response
					if (!result.WasSuccessful)
						return Response<IEnumerable<Image>>.CreateResponse(false, new List<Image>(), result.ErrorMessage);
					// No images in the list yet, add all the results
					if (imagesFound == null)
						imagesFound = result.Results;
					else
					{
						// Add only the images the match the previous colors as well
						var ids = imagesFound.Select(img => img.Id);
						imagesFound = result.Results.Where(img => ids.Contains(img.Id));
					}
				}
				// Return the images that were found
				return Response<IEnumerable<Image>>.CreateResponse(true, imagesFound ?? new List<Image>(), "");
			}
			catch (Exception ex)
			{
				// An exception occured, return the message
				DAL.DataServer.LogException(ex.Message);
				return Response<IEnumerable<Image>>.CreateResponse(false, new List<Image>(), "Invalid color definitions");
			}
		}

		/// <summary>
		/// Returns a response containing the images that match the pattern and tolerance
		/// </summary>
		/// <param name="tolerance">The tolerance level to match the images</param>
		/// <param name="patternId">The pattern to search by</param>
		/// <returns>A response containing the images matching the criteria</returns>
		[HttpGet]
		public Response<IEnumerable<Image>> Pattern(int tolerance, long patternId)
		{
			// Tolerance must be greater than zero
			if (tolerance <= 0)
				return Response<IEnumerable<Image>>.CreateResponse(false, new List<Image>(), "Invalid tolerance");
			// Check if the pattern Id is a valid pattern
			var existingPatternResponse = DAL.DataServer.Find<PatternDescription>(
				new Predicate("Id", patternId, PredicateOperator.EqualTo));
			// Return the exception if an error occured
			if (!existingPatternResponse.WasSuccessful)
				return Response<IEnumerable<Image>>.CreateResponse(false, new List<Image>(), existingPatternResponse.ErrorMessage);

			if (existingPatternResponse.Results.Count < 1)
			{
				// Return a response that the pattern was invalid
				return Response<IEnumerable<Image>>.CreateResponse(false, new List<Image>(),
					string.Format("There was no pattern found with the ID of {0}", patternId));
			}
			// Find the images that are public and have the pattern
			return Response<IEnumerable<Image>>.CreateResponse(true,
				DAL.DataServer.FindByJoin(typeof(Image), typeof(ImagePattern),
				new PredicateGroup(new[] { 
					new Predicate("IsPrivate", false, PredicateOperator.EqualTo),
					new Predicate("IsFolderPrivate", false, PredicateOperator.EqualTo),
					new Predicate("ImagePattern.PatternDescriptionId", patternId, PredicateOperator.EqualTo)
				}, true)).Results.Cast<Image>().ToList());
		}
	}

	/// <summary>
	/// Parses a string to have a color with the RGB value and the 
	/// </summary>
	internal class SearchForColor : Color.ColorDistanceVector
	{
		/// <summary>
		/// Creates a search color with the specified RGB values
		/// </summary>
		/// <param name="r">The red value</param>
		/// <param name="g">The green value</param>
		/// <param name="b">The blue value</param>
		private SearchForColor(int r, int g, int b)
			: base(r, g, b)
		{

		}

		/// <summary>
		/// Gets the tolerance of the searchable color
		/// </summary>
		public int Tolerance { get; private set; }
		/// <summary>
		/// Gets the percent match value of the searchable color
		/// </summary>
		public int Percent { get; private set; }

		/// <summary>
		/// Parses the string value to return a search color value
		/// </summary>
		/// <param name="value">The string representation of the search color</param>
		/// <returns>A search color object</returns>
		internal static SearchForColor Parse(string value)
		{
			// Split the values in the color string
			var values = value.Replace('[', ' ').Replace(']', ' ').Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
			// Parse the values and return the object
			var color = new SearchForColor(
				Convert.ToInt32(values[0].Substring(0, 2), 16),
				Convert.ToInt32(values[0].Substring(2, 2), 16),
				Convert.ToInt32(values[0].Substring(4, 2), 16))
			{
				Tolerance = int.Parse(values[2]),
				Percent = int.Parse(values[1])
			};
			return color;
		}
	}
}
